/** 
 * 动态路由 addrouter
 * @author
 * @version 2.0
 * @param baseUrl
 * @param router
 * @param store 
 * @param formatUrl
 * @param cleanArray
 * @param obj2param
 * @param downloadfile
 * >>> 待修改项：不支持fetch请求时，改用axios或原生
 */
const baseUrl = process.env.VUE_APP_URL

import router from '@/router'
import store from '../store'

export function formatUrl(url, data) {
  data = data || {}
  let param = []
  let subparam = []
  for (let key in data) {
    param.push(key + '=' + encodeURIComponent(data[key]))
    let reg = new RegExp('([&]?' + key + '=)[^&?]*', 'ig')
    if (reg.test(url)) {
      url = url.replace(reg, '$1' + encodeURIComponent(data[key]))
    } else {
      subparam.push(key + '=' + encodeURIComponent(data[key]))
    }
  }
  return url + ((url.indexOf('?') < 0) ? '?' + param.join('&') : (subparam.length > 0 && /.*\?\w+/.test(url) ? '&' : '') + subparam.join('&'))
}

function cleanArray(actual) {
  const newArray = []
  for (let i = 0; i < actual.length; i++) {
    if (actual[i]) {
      newArray.push(actual[i])
    }
  }
  return newArray
}

function obj2param(json) {
  if (!json) return ''
  return cleanArray(
    Object.keys(json).map(key => {
      if (json[key] === undefined) return ''
      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
    })
  ).join('&')
}

export function downloadfile(url, time) {
  let elem = document.createElement('iframe')
  elem.src = url
  elem.style.display = 'none'
  document.body.appendChild(elem)
  setTimeout(function (elem) {
    document.body.removeChild(elem)
  }, time, elem)
}

/* 请求失败时，显示错误提示 【 3秒内不重复提示 】 */
function showRequestErrorMsg({
  title,
  message,
  type
}, cb) {
  var lastExpireTm = sessionStorage.getItem('lastExpireTm')
  if (!lastExpireTm || ((new Date().getTime() - parseInt(lastExpireTm)) > 3 * 1000)) {
    router.app.$notify({
      title: title || "请求失败",
      type: type || 'error',
      message: message || "请求失败"
    })
    sessionStorage.setItem('lastExpireTm', new Date().getTime())

    typeof cb == "function" && cb()
  }
}

export default async (url = '', data = {}, type = 'GET', method = 'fetch') => {
  let noThrowError = data.noThrowError
  delete data.noThrowError
  type = type.toUpperCase()
  url = baseUrl + url

  // 拼接GET请求的完整接口路径
  if (type === 'GET' || type === 'HREF10' || type === 'HREF30') {
    let dataStr = ''
    Object.keys(data).forEach(key => {
      dataStr += key + '=' + data[key] + '&'
    })

    if (dataStr !== '') {
      dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'))
      url = url + '?' + dataStr
    }
  }

  // 如果浏览器支持fetch，就用fetch发起请求
  if (window.fetch && method === 'fetch') {
    let requestConfig = {
      // 浏览器缓存，如有新或旧的匹配，将从缓存中返回。如不匹配，浏览器将发出请求，并下载资源更新缓存。
      cache: 'force-cache', // *default, no-cache, reload, force-cache, only-if-cached
      // 无论是不是跨域请求, 总是发送请求资源域在本地的 cookies、 HTTP Basic authentication 等验证信息.
      credentials: 'include', // include, same-origin, *omit
      headers: new Headers({
        'Accept': '*/*',
        'Content-Type': 'application/json',
        'Authorization': store.state.user.token || ''
      }),
      method: type,
      mode: "cors",
      // mode: 'cors' // no-cors, cors, *same-origin
      // redirect: 'manual', // manual, *follow, error
      // referrer: 'no-referrer', // *client, no-referrer
    }

    if (type === 'POST') {
      // 如果是post请求，就在对象requestConfig新增一个自定义属性，body:JSON.stringify(data)
      Object.defineProperty(requestConfig, 'body', { // must match 'Content-Type' header
        value: JSON.stringify(data) // 对象类型转换为字符串类型
      })
    }

    /* POST && 使用form-data上传 */
    if (type === 'POST-FORMDATA') {
      requestConfig['headers'] = new Headers({
        'Accept': '*/*',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': store.state.user.token
      })
      requestConfig['method'] = 'POST'
      requestConfig['body'] = obj2param(data)
    }

    // 登陆需要一个特殊的header
    if (url.indexOf("auth/oauth/token") >= 0) {
      requestConfig['headers'] && requestConfig['headers'].set("Authorization", 'Basic bWNsb3VkOm1jbG91ZDg1MzEwMDQ5')
    }

    // 上传图片
    if (type === 'PUT') {
      requestConfig['method'] = 'POST' /* 先使用 post 上传 */
      requestConfig['headers'] = new Headers({
        'Accept': '*/*',
        'Authorization': store.state.user.token
      })

      let fd = new FormData()

      for (var i in data) {
        fd.append("file", data[i])
        // fd.append(i, data[i])
      }

      requestConfig['body'] = fd
    }
    url = formatUrl(url, {
      rn: Math.random()
    })
    /* 将问号后的全部加密 【验证码不加密】 */
    if (url.indexOf('?') >= 0 && url.indexOf('common/validImage') < 0) {
      let baseUrl = url.slice(0, url.indexOf('?'))
      let paramStr = url.slice(url.indexOf('?') + 1)
      // url = baseUrl + '?' + transToEncryptData(paramStr)
      url = baseUrl + '?' + paramStr
    }

    /* 特殊处理需要链接跳转的接口 如：导出接口 */
    if (type === 'HREF10') {
      // location.href = url
      downloadfile(url, 10000 * 60) // 防止有些响应过慢，还没有响应就被删了
      return
    }
    if (type === 'HREF30') {
      downloadfile(url, 30000 * 60)
      return
    }


    /* 特殊处理需要链接跳转的接口 如：验证码图片 */
    if (type === 'URL') {
      return url
    }

    let response
    try {
      response = await fetch(url, requestConfig)
      const responseJson = await response.json()

      // 不需要提示，自己处理错误 【 前提是服务端返回response中的body数据正常，不正常会被catch 】
      if (noThrowError && responseJson) {
        return responseJson
      }
      // 登陆token超时，自定义错误提示【会被noThrowError阻断】
      if ((responseJson && responseJson.code == 10000) || response.status == 401) {
        showRequestErrorMsg({
          title: '身份验证信息失效',
          message: '请重新登陆'
        }, () => {
          store.commit('clearInfos');
          store.dispatch('permission/FETCH_PERMISSION').then(() => {
            router.push('/')
          })
        })
        throw new Error('身份验证信息失效')
      }
      // 忘记密码获取验证码失败, 自定义错误提示
      else if (responseJson && responseJson.code == 902) {
        router.app.$notify({
          title: '验证码错误',
          message: '验证码已失效或填写错误',
          type: 'error'
        })
      }
      // 账号禁用
      else if (responseJson && responseJson.code == 907) {
        showRequestErrorMsg({
          title: '该账号已被禁用',
          type: 'error',
          message: responseJson.message
        }, () => {
          store.commit('clearInfos');
          store.dispatch('permission/FETCH_PERMISSION').then(() => {
            router.push('/')
          })
        })
        throw new Error('账号禁用提示')
      }
      // token过期
      else if (responseJson && responseJson.code == 401) {
        showRequestErrorMsg({
          title: '身份验证信息失效',
          message: '请重新登陆'
        }, () => {
          store.commit('clearInfos')
          store.dispatch('permission/FETCH_PERMISSION').then(() => {
            router.push('/')
          })
        })
      }
      // 其他类型请求失败，后端返回提示
      else if (responseJson && responseJson.code == -1) {
        if (response && response.status !== 200) {
          showRequestErrorMsg({
            title: '请求失败',
            type: 'error',
            message: `${response.status}[${response.statusText}]`
          })
        } else {
          showRequestErrorMsg({
            title: '请求失败',
            type: 'error',
            message: responseJson.message
          })
        }

        return responseJson
      }

      // 返回数据
      return responseJson
    } catch (error) {
      // todo 目前登陆失效、登陆密码错误、禁用、验证码错误等都会返回401。区别是登陆失效时，response的body中数据格式不对
      // http code为401时，fetch.json()出错会被catch
      if (response.status == 401) {
        showRequestErrorMsg({
          title: '身份验证信息失效',
          message: '请重新登陆'
        }, () => {
          store.commit('clearInfos');
          store.dispatch('permission/FETCH_PERMISSION').then(() => {
            router.push('/')
          })
        })
        throw new Error('身份验证信息失效')
      }

      // 处理其他的 http code 错误，以及无法解析成json的情况【报出错后，返回到业务代码中，自行处理】
      else if (response && response.status !== 200) {
        showRequestErrorMsg({
          title: '请求失败',
          type: 'error',
          message: `${response.status}[${response.statusText}]`
        })
        return Promise.resolve({
          code: -9999,
          status: response.status,
          message: response.statusText,
        })
      }

      throw new Error(error)
    }
  } else { // 如果浏览器不支持fetch，就用原生的XMLHttpRequest发起请求
    return new Promise((resolve, reject) => {
      let requestObj
      if (window.XMLHttpRequest) {
        requestObj = new XMLHttpRequest()
      } else {
        requestObj = new ActiveXObject()
      }

      let sendData = ''
      if (type === 'POST') {
        sendData = JSON.stringify(data) // 对象转为字符串
      }

      requestObj.open(type, url, true)
      requestObj.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
      requestObj.send(sendData) // 如果不是post请求，sendData就为空，也就是发起了get请求。

      requestObj.onreadystatechange = () => {
        if (requestObj.readyState === 4) {
          if (requestObj.status === 200) {
            let obj = requestObj.response
            if (typeof obj !== 'object') { // 如果请求结果不是json对象，就把强制转为json对象
              obj = JSON.parse(obj)
            }
            resolve(obj) // 返回请求结果
          } else {
            reject(requestObj)
          }
        }
      } 
    })
  }
}
